package org.dromara.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.BeanUtils;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.PayOrderLog;
import org.dromara.system.elastic.EsQueryType;
import org.dromara.system.elastic.entity.PayOrderEs;
import org.dromara.system.kernel.OrderSearchKernel;
import org.dromara.system.mapper.PayOrderLogMapper;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.stereotype.Service;
import org.dromara.system.domain.bo.PayOrderBo;
import org.dromara.system.domain.vo.PayOrderVo;
import org.dromara.system.domain.PayOrder;
import org.dromara.system.mapper.PayOrderMapper;
import org.dromara.system.service.IPayOrderService;

import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 订单Service业务层处理
 *
 * @author Gavino
 * @date 2024-10-22
 */
@Slf4j
@RefreshScope
@RequiredArgsConstructor
@Service
public class PayOrderServiceImpl implements IPayOrderService {

    private final PayOrderMapper baseMapper;
    private final PayOrderLogMapper logMapper;
    private final ElasticsearchTemplate restTemplate;
    private final OrderSearchKernel orderSearchKernel;
    @Value("${spring.elasticsearch.environment}")
    private String environment;
    @Value("${queryByEs}")
    private Boolean queryByEs;

    @Override
    public PayOrder selectById(Long orderId) {
        // 执行查询
        PayOrderEs orderEs = null;
        if (queryByEs) {
            orderEs = restTemplate.get(orderId.toString(), PayOrderEs.class, IndexCoordinates.of("pay_order_" + environment));
        }

        if (orderEs == null) {
            return baseMapper.selectById(orderId);
        }
        return BeanUtils.copy(orderEs, PayOrder.class);
    }

    /**
     * 查询订单
     *
     * @param orderId 主键
     * @return 订单
     */
    @Override
    public PayOrderVo queryById(Long orderId) {
        return baseMapper.selectVoById(orderId);
    }

    /**
     * 分页查询订单列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 订单分页列表
     */
    @Override
    public TableDataInfo<PayOrderVo> queryPageList(PayOrderBo bo, PageQuery pageQuery) {

        if (queryByEs) {
            List<SortBuilder<?>> sorts = new ArrayList<>();
            sorts.add(SortBuilders.fieldSort("orderId").order(SortOrder.DESC)); //根据订单ID倒叙
            PageImpl<PayOrderVo> page = orderSearchKernel.queryOrderEsPage(orderSearchKernel.buildQueryBuilder(bo), pageQuery.getPageNum() - 1, pageQuery.getPageSize(), PayOrderVo.class, sorts, Arrays.stream(PayOrderVo.class.getDeclaredFields()).map(Field::getName).toArray(String[]::new));
            TableDataInfo<PayOrderVo> rspData = new TableDataInfo<>();
            rspData.setCode(200);
            rspData.setRows(page.getContent());
            rspData.setMsg("查询成功");
            rspData.setTotal(page.getTotalElements());
            return rspData;
        }

        LambdaQueryWrapper<PayOrder> lqw = buildQueryWrapper(bo);
        lqw.orderByDesc(PayOrder::getOrderId);
        Page<PayOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);

        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的订单列表
     *
     * @param bo 查询条件
     * @return 订单列表
     */
    @Override
    public <C> List<C> queryList(PayOrderBo bo, Class<C> voClass) {
        if (queryByEs) {
            List<SortBuilder<?>> sorts = new ArrayList<>();
            sorts.add(SortBuilders.fieldSort("orderId").order(SortOrder.DESC)); //根据订单ID倒叙
            PageImpl<C> page = orderSearchKernel.queryOrderEsPage(orderSearchKernel.buildQueryBuilder(bo), null, null, voClass, sorts, Arrays.stream(voClass.getDeclaredFields()).map(Field::getName).toArray(String[]::new));
            return page.getContent();
        }

        LambdaQueryWrapper<PayOrder> lqw = buildQueryWrapper(bo);
        lqw.orderByDesc(PayOrder::getOrderId);
        return baseMapper.selectVoList(lqw, voClass);
    }

    private LambdaQueryWrapper<PayOrder> buildQueryWrapper(PayOrderBo bo) {
        //可见权限
        if (LoginHelper.isAgent()) {
            bo.setAgentId(LoginHelper.getUserId());
        }
        if (LoginHelper.isClient()) {
            bo.setClientId(LoginHelper.getUserId());
        }

        //构建查询条件
        LambdaQueryWrapper<PayOrder> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getAppId() != null, PayOrder::getAppId, bo.getAppId());
        lqw.eq(bo.getPayTypeId() != null, PayOrder::getPayTypeId, bo.getPayTypeId());
        lqw.eq(bo.getOrderType() != null, PayOrder::getOrderType, bo.getOrderType());
        lqw.eq(StringUtils.isNotBlank(bo.getOrderAid()), PayOrder::getOrderAid, bo.getOrderAid());
        lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), PayOrder::getOrderNo, bo.getOrderNo());
        lqw.eq(StringUtils.isNotBlank(bo.getOrderCid()), PayOrder::getOrderCid, bo.getOrderCid());
        lqw.eq(StringUtils.isNotBlank(bo.getForceCurrency()), PayOrder::getForceCurrency, bo.getForceCurrency());
        lqw.eq(StringUtils.isNotBlank(bo.getFromDomain()), PayOrder::getFromDomain, bo.getFromDomain());
        lqw.eq(StringUtils.isNotBlank(bo.getFormIp()), PayOrder::getFormIp, bo.getFormIp());
        lqw.eq(StringUtils.isNotBlank(bo.getCurrency()), PayOrder::getCurrency, bo.getCurrency());

        lqw.eq(bo.getPayStatus() != null, PayOrder::getPayStatus, bo.getPayStatus());
        lqw.eq(bo.getStatus() != null, PayOrder::getStatus, bo.getStatus());
        lqw.eq(bo.getIsFeign() != null, PayOrder::getIsFeign, bo.getIsFeign());
        lqw.eq(bo.getRefundStatus() != null, PayOrder::getRefundStatus, bo.getRefundStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getRefundId()), PayOrder::getRefundId, bo.getRefundId());
        lqw.eq(StringUtils.isNotBlank(bo.getDisputeId()), PayOrder::getDisputeId, bo.getDisputeId());
        lqw.eq(bo.getGoodsType() != null, PayOrder::getGoodsType, bo.getGoodsType());
        lqw.like(StringUtils.isNotBlank(bo.getCustomerName()), PayOrder::getCustomerName, bo.getCustomerName());
        lqw.eq(StringUtils.isNotBlank(bo.getCustomerPhone()), PayOrder::getCustomerPhone, bo.getCustomerPhone());
        lqw.eq(StringUtils.isNotBlank(bo.getCustomerEmail()), PayOrder::getCustomerEmail, bo.getCustomerEmail());
        lqw.eq(StringUtils.isNotBlank(bo.getCustomerIp()), PayOrder::getCustomerIp, bo.getCustomerIp());
        lqw.eq(StringUtils.isNotBlank(bo.getPhoneCountry()), PayOrder::getPhoneCountry, bo.getPhoneCountry());
        lqw.eq(StringUtils.isNotBlank(bo.getIpCountry()), PayOrder::getIpCountry, bo.getIpCountry());
        lqw.eq(bo.getTrackStatus() != null, PayOrder::getTrackStatus, bo.getTrackStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getTrackNo()), PayOrder::getTrackNo, bo.getTrackNo());
        lqw.eq(StringUtils.isNotBlank(bo.getSessionId()), PayOrder::getSessionId, bo.getSessionId());
        lqw.eq(bo.getAccountId() != null, PayOrder::getAccountId, bo.getAccountId());
        lqw.like(StringUtils.isNotBlank(bo.getAccountName()), PayOrder::getAccountName, bo.getAccountName());
        lqw.eq(bo.getClientId() != null, PayOrder::getClientId, bo.getClientId());
        lqw.eq(bo.getAgentId() != null, PayOrder::getAgentId, bo.getAgentId());
        lqw.eq(StringUtils.isNotBlank(bo.getRemark()), PayOrder::getRemark, bo.getRemark());

        //处理日期范围查询
        Map<String, Object> params = bo.getParams();
        lqw.between(params.get("beginPayTime") != null && params.get("endPayTime") != null,
            PayOrder::getPayTime, params.get("beginPayTime"), params.get("endPayTime"));
        lqw.between(params.get("beginUpdateTime") != null && params.get("endUpdateTime") != null,
            PayOrder::getUpdateTime, params.get("beginUpdateTime"), params.get("endUpdateTime"));
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            PayOrder::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));

        //商户不看创建失败订单
        lqw.ne(LoginHelper.isClient(), PayOrder::getStatus, 0);

        return lqw;
    }

    /**
     * 新增订单
     *
     * @param order 订单
     * @return 是否新增成功
     */
    @Override
    public PayOrder insertOrder(PayOrder order) {

        baseMapper.insert(order);

        List<PayOrderEs> payOrders = new ArrayList<>();
        payOrders.add(BeanUtils.copy(order, PayOrderEs.class));
        insertOrderToEs(payOrders);

        return order;
    }

    /**
     * 更新订单，同步更新ES
     *
     * @param order 订单
     * @return 结果
     */
    public Boolean updatePayOrder(PayOrder order) {
        int num = baseMapper.updateById(order);
        if (num > 0) {
            updateOrderEs(order.getOrderId());
        }
        return num > 0;
    }

    /**
     * 更新订单，同步更新ES
     *
     * @param queryWrapper 条件
     * @return 结果
     */
    public Boolean updatePayOrder(LambdaUpdateWrapper<PayOrder> queryWrapper) {
        int num = baseMapper.update(queryWrapper);
        if (num > 0) {
            List<PayOrder> orders = baseMapper.selectList(queryWrapper);
            Lists.partition(orders, 1000).forEach(tmpList -> insertOrderToEs(BeanUtils.copyList(tmpList, PayOrderEs.class)));
        }
        return num > 0;
    }

    /**
     * 更新订单，同步更新ES
     *
     * @param orders 订单
     * @return 结果
     */
    public void updatePayOrderBatch(List<PayOrder> orders) {
        Lists.partition(orders, 1000).forEach(tmpList -> {
            boolean stat = baseMapper.updateBatchById(tmpList);
            if (stat) {
                List<PayOrder> newList = baseMapper.selectBatchIds(tmpList.stream().map(PayOrder::getOrderId).toList());
                insertOrderToEs(BeanUtils.copyList(newList, PayOrderEs.class));
            }
        });
    }

    /**
     * 异步更新订单
     *
     * @param order 订单
     * @return 结果
     */
    public CompletableFuture<Boolean> updatePayOrderAsync(PayOrder order) {
        return CompletableFuture.supplyAsync(() -> updatePayOrder(order));
    }

    /**
     * 更新Es订单
     *
     * @param orderId 订单ID
     */
    void updateOrderEs(Long orderId) {
        try {
            PayOrder order = baseMapper.selectById(orderId);

            PayOrderEs esObj = BeanUtils.copy(order, PayOrderEs.class);

            initEsField(esObj);//初始化ES字段

            // 更新 Elasticsearch 中的文档
            IndexQuery indexQuery = new IndexQueryBuilder().withId(String.valueOf(order.getOrderId())).withObject(esObj).build();
            restTemplate.index(indexQuery, IndexCoordinates.of("pay_order_" + environment));
        } catch (Exception e) {
            if (e.getMessage() == null || !e.getMessage().contains("200 OK")) {
                log.error("更新Es订单 异常", e);
            }
        }
    }

    /**
     * 插入数据到Es
     *
     * @param payOrders 订单列表
     * @return 结果
     */
    private Boolean insertOrderToEs(List<PayOrderEs> payOrders) {
        List<IndexQuery> indexQueries = new ArrayList<>();

        for (PayOrderEs payOrder : payOrders) {

            initEsField(payOrder);//初始化ES字段

            IndexQuery indexQuery = new IndexQueryBuilder().withObject(payOrder).build();
            indexQueries.add(indexQuery);
        }
        try {
            restTemplate.bulkIndex(indexQueries, IndexCoordinates.of("pay_order_" + environment));
        } catch (Exception e) {
            if (e.getMessage() != null && e.getMessage().contains("200 OK")) {
                return Boolean.TRUE;
            } else {
                log.error("插入数据到Es 异常", e);
                return Boolean.FALSE;
            }
        }
        return Boolean.TRUE;
    }

    /**
     * 初始化ES字段
     *
     * @param orderEs es对象
     */
    private void initEsField(PayOrderEs orderEs) {
        orderEs.setCreateYear(orderEs.getCreateTime().getYear() + 1900);
        orderEs.setCreateMonth(orderEs.getCreateTime().getMonth() + 1);
        orderEs.setCreateDay(orderEs.getCreateTime().getDate());
        orderEs.setCreateDate(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, orderEs.getCreateTime()));
        if (orderEs.getSett() == null) {
            orderEs.setSett(0);
        }
        if(StringUtils.isNotBlank(orderEs.getLinkCode())) {
            orderEs.setFromDomain(orderEs.getLinkCode());
        }
    }

    /**
     * 校验并批量删除订单信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (ids.isEmpty()) {
            return Boolean.TRUE;
        }
        LambdaQueryWrapper<PayOrder> query = new LambdaQueryWrapper<>();
        query.in(PayOrder::getOrderId, ids);

        if (isValid) {
            if (LoginHelper.isAgent()) {
                query.eq(PayOrder::getAgentId, LoginHelper.getUserId());
            }
            if (LoginHelper.isClient()) {
                query.eq(PayOrder::getClientId, LoginHelper.getUserId());
            }
        }

        boolean status = baseMapper.delete(query) > 0;

        if(status) {
            //删除订单日志
            logMapper.delete(new LambdaQueryWrapper<PayOrderLog>().in(PayOrderLog::getOrderId, ids));
            //删除es订单
            for (Long orderId : ids) {
                try {
                    restTemplate.delete(orderId.toString(), IndexCoordinates.of("pay_order_" + environment));
                } catch (Exception e) {
                    if (e.getMessage() == null || !e.getMessage().contains("200 OK")) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return status;
    }

    @Override
    public Long getRealSuccessNum(Long appId, Date ratioTime) {

        return baseMapper.selectCount(new LambdaQueryWrapper<PayOrder>()
            .eq(PayOrder::getAppId, appId)
            .ge(PayOrder::getCreateTime, ratioTime)
            .and(wrapper -> wrapper
                .eq(PayOrder::getStatus, 2)
                .or()
                .eq(PayOrder::getIsFeign, 1)
            ));
    }

    @Override
    public Long getIsFeignNum(Long appId, Date ratioTime) {
        return baseMapper.selectCount(new LambdaQueryWrapper<PayOrder>()
            .eq(PayOrder::getAppId, appId)
            .ge(PayOrder::getCreateTime, ratioTime)
            .eq(PayOrder::getIsFeign, 1));
    }

    /**
     * 同步订单数据到ES
     */
    @Override
    public R<?> syncOrderToEs(Long orderId) {
        //更新单个订单对象
        if (orderId != null && orderId > 0) {
            updateOrderEs(orderId);
            return R.ok("更新成功");
        }

        int pageSize = 3000; // 每页查询数据量
        int pageNum = 1; // 起始页码
        int totalSynced = 0; // 已同步数据条数

        ExecutorService executor = Executors.newFixedThreadPool(5);

        while (true) {
            QueryWrapper<PayOrder> wrapper = new QueryWrapper<>();
            wrapper.last("limit " + (pageNum - 1) * pageSize + "," + pageSize);
            List<PayOrder> payOrders = baseMapper.selectList(wrapper);

            if (payOrders.isEmpty()) { // 如果查询结果为空，则退出循环
                break;
            }

            executor.submit(() -> {
                return insertOrderToEs(BeanUtils.copyList(payOrders, PayOrderEs.class)); // 批量保存数据到 Elasticsearch
            });

            totalSynced += payOrders.size();
            // 记录已同步数据条数
            System.out.println("已提交 " + totalSynced + " 条数据至Elasticsearch");

            pageNum++; // 准备查询下一页数据
        }

        return R.ok("已同步" + totalSynced + "条数据");
    }

    /**
     * 清理订单
     *
     * @param time 保留这个时间后的数据
     * @return 结果
     */
    @Override
    public R<?> cleanOrder(String time) {
        Date date;
        if (StringUtils.isNotEmpty(time)) {
            try {
                date = DateUtils.parseDate(time, DateUtils.YYYY_MM_DD_HH_MM_SS); // 解析结束时间字符串
            } catch (ParseException e) {
                return R.fail("【清理订单】执行失败，日期解析异常");
            }
        } else {
            // 获取当前时间
            LocalDateTime currentTime = LocalDateTime.now();
            // 将当前时间减去三个月
            Instant instant = currentTime.minusMonths(6).atZone(ZoneId.systemDefault()).toInstant();
            date = Date.from(instant);
        }

        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
        queryBuilder.filter(QueryBuilders.rangeQuery("createTime").lt(date.getTime()));
        PageImpl<PayOrderVo> page = orderSearchKernel.queryOrderEsPage(queryBuilder, null, null, PayOrderVo.class, null, Arrays.stream(PayOrderVo.class.getDeclaredFields()).map(Field::getName).toArray(String[]::new));
        List<Long> orderIds = page.getContent().stream().map(PayOrderVo::getOrderId).toList();

        //删除订单
        boolean status = deleteWithValidByIds(orderIds, false);

        return status ? R.ok() : R.fail();
    }
}
